package com.yunbao.ioc.factory;

import com.yunbao.ioc.bd.*;
import com.yunbao.ioc.io.Resources;
import com.yunbao.ioc.tx.TransactionalAdviceInfo;

import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Stream;

/**
 * Created by louisyuu on 2020/3/3 下午2:25
 */
public class DefaultBeanFactory implements BeanFactory {

    /**
     * id 对象实例池
     */
    private static final Map<String, Object> beans = new HashMap<>();


    /**
     * 类型对象集合实例池
     */
    private static final Map<String, List<Object>> beansOfType = new HashMap<>();


    /**
     * 对象锁
     */
    private final Object LOAD_OBJECT = new Object();


    private volatile boolean isLoad = false;

    /**
     * Class path
     */
    private String path;

    /**
     * 注解扫码的包名
     */
    private String[] scanPackageNames;


    private BeanDefinitionRegistry beanDefinitionRegistry;


    public DefaultBeanFactory(String path, String... scanPackageNames) {
        this.path = path;
        this.scanPackageNames = scanPackageNames;
        beanDefinitionRegistry = new DefaultBeanDefinitionRegistry();
        if (!isLoad) {
            synchronized (LOAD_OBJECT) {
                isLoad = true;
                refresh();
            }
        }
    }

    public <T> T getBean(String name, Class<T> clazz) {
        if (clazz == null || name == null) {
            throw new RuntimeException("Params are required");
        }
        return (T) beans.get(name);
    }

    public Object getBean(String name) {
        if (name == null) {
            throw new RuntimeException("Params are required");
        }
        return beans.get(name);
    }


    @Override
    public Collection<Object> getBeans() {
        return beans.values();
    }


    @Override
    public <T> List<T> getBeans(Class<T> type) {
        return (List<T>) beansOfType.get(type.getName());
    }


    protected void refresh() {
        //加载资源，从Xml资源封装BeanDefinition
        if (this.path != null) {
            InputStream is = Resources.getResourceAsStream(path);
            XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanDefinitionRegistry);
            xmlBeanDefinitionReader.loadBeanDefinitions(is);
        }


        if (this.scanPackageNames != null && scanPackageNames.length > 0) {
            //扫码包，从X注解资源封装BeanDefinition
            AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(beanDefinitionRegistry, scanPackageNames);
            annotatedBeanDefinitionReader.loadBeanDefinitions();
        }


        //实例化bean
        beanInstantiation();

        //给bean注入属性
        populateBeans();

        //查看是否需要生成代理对象，如果需要就生成
        proxyInstantiation();


    }

    protected void beanInstantiation() {
        Collection<BeanDefinition> beanDefinitions = beanDefinitionRegistry.getBeanDefinitions();
        beanDefinitions.forEach(bd -> {
            try {
                Object bean = bd.getBeanClass().newInstance();
                beans.put(bd.getId(), bean);

                Class<?>[] interfaces = bd.getBeanClass().getInterfaces();
                if (interfaces != null && interfaces.length > 0) {
                    appendBeans(bean, interfaces);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }


    private void appendBeans(Object bean, Class<?>[] beanOfInterfaces) {
        Stream.of(beanOfInterfaces).forEach(e -> {
            List<Object> objects = beansOfType.get(e.getName());
            if (objects == null || objects.isEmpty()) {
                objects = new ArrayList<>();
            }
            objects.add(bean);

            beansOfType.put(e.getName(), objects);


        });
    }


    protected void populateBeans() {
        Collection<BeanDefinition> beanDefinitions = beanDefinitionRegistry.getBeanDefinitions();
        beanDefinitions.forEach(bd -> {
            Object bean = getBean(bd.getId(), bd.getBeanClass());

            if (!bd.getPropertyValues().isEmpty()) {
                List<PropertyValue> propertyValues = bd.getPropertyValues();
                propertyValues.forEach(pv -> {
                    try {
                        Field field = bean.getClass().getDeclaredField(pv.getName());
                        field.setAccessible(true);


//                        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(pv.getName(), bd.getBeanClass());
                        if (pv.getValueType().equals(PropertyValue.ValueType.REFERENCE)) {
                            //默认按照名称注入
//                            propertyDescriptor.getWriteMethod().invoke(bean, getBean(pv.getName()));
                            field.set(bean, getBean(pv.getName()));
                        } else {
//                            propertyDescriptor.getWriteMethod().invoke(bean, pv.getValue());
                            field.set(bean, pv.getValue());

                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                });
                beans.put(bd.getId(), bean);
            }
        });

    }


    protected void proxyInstantiation() {
        beans.forEach((beanName, bean) -> {
            Set<Method> adviceMethods = TransactionalAdviceInfo.getAdviceMethods(bean.getClass());
            if (adviceMethods != null && !adviceMethods.isEmpty()) {
                ProxyFactory proxyFactory = getBean("proxyFactory", ProxyFactory.class);
                if (bean.getClass().getInterfaces().length > 0) {
                    proxyFactory.getJdkProxy(bean);
                } else {
                    proxyFactory.getCglibProxy(bean);
                }
            }


        });
    }


}
